package scalaExec.scalaLab;

import java.awt.Font;
import scalaExec.ClassLoaders.JarClassLoader;
import scalaExec.Interpreter.GlobalValues;
import scalaExec.gui.GBC;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;


// a class to handle ScalaSci toolboxes
import javax.swing.JTextArea;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;


// this class updates the system in order to consider the additional .jar toolboxes placed at the "Available" toolboxes list
    
public class scalaSciToolboxes {

        public   static JList  scalaSciToolboxesAvailableList;    // the available toolboxes for ScalaSci as full .jar pathnames
        public   static JList  scalaSciToolboxesLoadedList;   // the list of already loaded toolboxes as full .jar pathnames
        public   static DefaultListModel  scalaSciToolboxesAvailableListModel;   // the ListModel that keeps the data displayed at the scalaSciToolboxesAvailable list
        public   static DefaultListModel  scalaSciToolboxesLoadedListModel; // the ListModel that keeps the data displayed at the scalaSciToolboxesAvailable list
        public   static JPanel scalaSciToolboxesPanel = new JPanel(new GridBagLayout());
        // keeps information on all loaded toolboxes. Each entry keeps the toolbox name as the full pathname of the .jar file of the toolbox and
        // a Vector that keeps the loaded classes of the toolbox
        public   static Vector <scalaSciToolbox>   ssciToolboxes = new Vector();

        public   static HashMap  <String, JFrame> framesOfToolboxClasses  = new HashMap();   // keeps track of the toolboxes frames in order to be able to remove them
        public   static int selectedAvailableToolboxIndex;   // the selected toolbox index by the user from the list of the available toolboxes
        public   static int selectedLoadedToolboxIndex;   // the selected toolbox index by the user from the list of the loaded toolboxes
        
        
   // handles the main tab panel for ScalaSci's toolboxes
   public static  JPanel  handleScalaSciTab()  {
       if (scalaSciToolboxesAvailableListModel == null)  {
          scalaSciToolboxesAvailableListModel = new DefaultListModel();   // the list of .jar files that will be available for ScalaSci toolboxes
                    // put the available toolboxes in a list
          scalaSciToolboxesAvailableList = new JList(scalaSciToolboxesAvailableListModel);
          int toolboxForLoadCnt = GlobalValues.ScalaSciToolboxes.size();   // the number of toolboxes available for loading
          if (toolboxForLoadCnt > 0)
              for (int k=0; k<toolboxForLoadCnt; k++)
                     scalaSciToolboxesAvailableListModel.addElement(GlobalValues.ScalaSciToolboxes.elementAt(k));
                  
          //scalaSciToolboxesAvailableList.addMouseListener(new MouseAdapterForSSIAvailableToolboxes());
          scalaSciToolboxesAvailableList.addListSelectionListener(new ListSelectionListener() {
    //    this listener tracks the toolbox selected by the user from the list
            @Override
            public void valueChanged(ListSelectionEvent event)
            {
               selectedAvailableToolboxIndex  = scalaSciToolboxesAvailableList.getSelectedIndex();
            }
        });
       }

       if (scalaSciToolboxesLoadedListModel == null)   {    // the list of .jar files that are already loaded as ScalaSci toolboxes
           scalaSciToolboxesLoadedListModel = new DefaultListModel(); 
           scalaSciToolboxesLoadedList = new JList(scalaSciToolboxesLoadedListModel);
           //scalaSciToolboxesLoadedList.addMouseListener(new MouseAdapterForSSILoadedToolboxes());
          scalaSciToolboxesLoadedList.addListSelectionListener(new ListSelectionListener() {
    //    this listener tracks the toolbox selected by the user from the list
            @Override
            public void valueChanged(ListSelectionEvent event)
            {  
               selectedLoadedToolboxIndex = scalaSciToolboxesLoadedList.getSelectedIndex();
            }
        });
       }
       
        //scalaSciToolboxesLoadedList
        JScrollPane availableToolboxesScrollPane = new JScrollPane(scalaSciToolboxesAvailableList);
        JScrollPane loadedToolboxesScrollPane = new JScrollPane(scalaSciToolboxesLoadedList);
        
          // the help panel displays the steps that the user has to follow in order to add toolboxes
        JTextArea helpArea = new JTextArea();
        helpArea.setFont(new Font("Arial", Font.BOLD, 11));
        helpArea.append("Help on basic toolbox operations: \n"+
                "\n  Add new toolboxes: \n\n"+
                "METHOD 1:  (The most reliable method) \n "+
                "Step 1. Unzip the .jar file of the toolbox \n"+
                "Step 2. Update the ScalaClassPath to include the root directory where the .jar file was extracted \n "+
                "\n\n METHOD 2: \n\n"+
                " Import the specified toolboxes in the 'Available Toolboxes' list with the 'Import toolboxes' button \n"+
                " Toolboxes can be removed from the 'Available Toolboxes' list with a right-mouse click\n\n"+
                "An installed toolbox during one Scalalab session, remains on the ScalaClassPath, \n"+
                "e.g. having installed weka.jar, weka.jar remains on the ClassPath for next sessions\n\n"+
                "\n  Remove toolboxes: \n"+
                "Step 1.  Select the toolbox from the loaded toolboxes list \n"+
                "Step 2.  Right-mouse click and select the remove option of the popup menu \n");
        
        JPanel helpPanel = new JPanel();
        helpPanel.add(new JScrollPane(helpArea));

        // the buttons for specifying and importing toolboxes
        JPanel buttonsPanel = new JPanel();   // keeps the control buttons together
        JButton loadBtn = new JButton("Import toolboxes");
        loadBtn.setToolTipText("Import the specified toolboxes");
        loadBtn.addActionListener(new updateSystemWithJarToolboxes());
        
        JButton permanentInstallBtn = new JButton("Install permanently");
        permanentInstallBtn.setToolTipText("Permanently installs a toolbox by merging the toolbox's classes within the Scalalab's jar file");
        permanentInstallBtn.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
               
        String  forScalaToolboxJarName = (String) scalaSciToolboxes.scalaSciToolboxesAvailableListModel.getElementAt(0);  // full path name of the toolbox JAR file
                try {
                    // full path name of the toolbox JAR file
                    scalaExec.ClassLoaders.JarClassLoader.appendZip(GlobalValues.jarFilePath, forScalaToolboxJarName);
                } catch (IOException ex) {
                    Logger.getLogger(scalaSciToolboxes.class.getName()).log(Level.SEVERE, null, ex);
                }
             } 
        });
               
         JButton addBtn = new JButton("Specify toolboxes");
        addBtn.setToolTipText("Specify additional toolboxes for Scala");
         
         JCheckBox loadAlsoMethodsCB = new JCheckBox("Retrieve also methods: ", GlobalValues.retrieveAlsoMethods);
         loadAlsoMethodsCB.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
          GlobalValues.retrieveAlsoMethods = ((JCheckBox)e.getSource()).isSelected();
            }
        });
        buttonsPanel.add(addBtn);
        buttonsPanel.add(loadBtn);
        buttonsPanel.add(loadAlsoMethodsCB);
        
        addBtn.addActionListener(new  ActionListener() {  // adds a toolbox

            public void actionPerformed(ActionEvent e) {
               JFileChooser  chooser = new JFileChooser(GlobalValues.workingDir);
               chooser.setDialogTitle("Specify JAR file containing Java classes for ScalaSci");
               int retVal = chooser.showOpenDialog(GlobalValues.scalalabMainFrame);
               if (retVal == JFileChooser.APPROVE_OPTION) {   // approve toolbox
                    File selectedFile = chooser.getSelectedFile();
                    String jarFileName = selectedFile.getAbsolutePath();
    if (jarFileName.endsWith(".jar") )  
        {  // if toolbox is not already specified insert it to the list of available toolboxes
           boolean toolboxFound = false;
           int loadedToolboxesCnt = scalaSciToolboxesLoadedListModel.size();
           for (int toolboxIdx=0; toolboxIdx<loadedToolboxesCnt; toolboxIdx++)  {  // search for the selected toolbox
               String currentToolboxName = (String) scalaSciToolboxesLoadedListModel.get(toolboxIdx);   // the name of the current toolbox
               if (currentToolboxName.equalsIgnoreCase(jarFileName) == true)   {  // toolbox found
                   toolboxFound = true;
                   break;
                 }
           }  // search for the selected toolbox
           
           if (toolboxFound == false)  {  // selected toolbox not already exists, thus insert it
        scalaSciToolboxesAvailableListModel.addElement(jarFileName);      // update the list model that keeps the jar files that are used as toolboxes

        GlobalValues.scalaJarClassesLoaded = false;  // reload classes
                          }
                 }  // if toolbox is not already specified 
              }  // approve toolbox
            }  // ActionPerformed
        });
                     
            scalaSciToolboxesPanel.add(helpPanel, new GBC(0, 0, 6, 1 ));
            scalaSciToolboxesPanel.add(buttonsPanel, new GBC(0, 1, 6, 1));
            scalaSciToolboxesPanel.add(new JLabel("Available Toolboxes for ScalaSci"), new GBC(0, 2, 1, 1));
            scalaSciToolboxesPanel.add(availableToolboxesScrollPane, new GBC(1, 2, 2, 1 ));
            scalaSciToolboxesPanel.add(new JLabel("Loaded Toolboxes for ScalaSci"), new GBC(3, 2, 1, 1));
            scalaSciToolboxesPanel.add(loadedToolboxesScrollPane, new GBC(4, 2, 2, 1));
            scalaSciToolboxesPanel.add(permanentInstallBtn, new GBC(1, 3, 2, 1));
  
  return scalaSciToolboxesPanel;
  
                    
     }
   
}



    class updateSystemWithJarToolboxes extends AbstractAction {
             updateSystemWithJarToolboxes() { super("Update system with jar toolboxes  "); }

     // this routine can be useful when we need to install the toolbox in the ../jre/ext/lib  folder, in order the classes of the toolbox to be accessible by the 
    // extension class loader. However, the extension class loader cannot resolve classes that depend on other libraries, e.g. the Scala library classes         
             private String extractJDKPath(String pathConfig) {
                    StringTokenizer strTok = new StringTokenizer(pathConfig, File.pathSeparator);
                    String jdkElem = null;
                    while (strTok.hasMoreTokens()) {
                        String currentPathElem = strTok.nextToken();
                        if (currentPathElem.contains("bin") && currentPathElem.contains("jdk")) {
                               jdkElem = currentPathElem;
                               break;
                        }
                    }
                        return jdkElem;
            }
                        
            private void copyFile(String source, String dest) {
                 try {
            FileInputStream fin = new FileInputStream(source);
            FileOutputStream fout = new FileOutputStream(dest);
                int c;
                while ((c = fin.read())!=-1) 
                        fout.write(c);
                fin.close();
                fout.close();
        
        } catch (FileNotFoundException ex) {
            Logger.getLogger(updateSystemWithJarToolboxes.class.getName()).log(Level.SEVERE, null, ex);
        }
                 catch (IOException ioex) {
                     Logger.getLogger(updateSystemWithJarToolboxes.class.getName()).log(Level.SEVERE, null, ioex);
                 }
            }
   
      // performs the operations needed in order the classes of the toolboxes to become available at the ScalaSci scripting engine
           public void actionPerformed(ActionEvent e) {
             
               if (GlobalValues.scalaJarClassesLoaded ==  false) {   // the classes of the toolboxes for Scala were not loaded
             GlobalValues.ScalaToolboxesLoader = new JarClassLoader();
          try {
           
           // for all the elements of the available toolboxes for ScalaSci load the corresponding toolbox
           int numToolboxes = scalaSciToolboxes.scalaSciToolboxesAvailableListModel.size();
           for (int k=0; k<numToolboxes; k++)    {  // for all toolboxes for ScalaSci
               int numClassesOfToolbox=0;
               String  forScalaToolboxJarName = (String) scalaSciToolboxes.scalaSciToolboxesAvailableListModel.getElementAt(k);  // full path name of the toolbox JAR file
               GlobalValues.currentToolboxName = forScalaToolboxJarName;   // keep the currently loaded toolbox globally
               scalaSciCommands.BasicCommands.appendClasspath(forScalaToolboxJarName);
               
                GlobalValues.ScalaToolboxesLoader = new JarClassLoader();
                GlobalValues.ScalaToolboxesLoader.extendClassPath(forScalaToolboxJarName);
                //  extend the extension class loader in order to allow inspecting toolbox objects
                GlobalValues.extensionClassLoader.extendClassPath(forScalaToolboxJarName);
                
                numClassesOfToolbox  = GlobalValues.ScalaToolboxesLoader.scanAllToolboxClasses(forScalaToolboxJarName);
             
                //System.out.println("Classes loaded  by ExtensionClassLoader ole = "+scalaExec.Functions.ExtensionClassLoader.classesAdditionalFnt);
              
                 // update structures for ScalaSciClassPath
       if (GlobalValues.ScalaSciClassPathComponents.contains(forScalaToolboxJarName)==false)        
               {    // path not already exist in ScalaSciClassPath
          GlobalValues.ScalaSciClassPathComponents.add(forScalaToolboxJarName);  // append to the ScalaSciClassPathComponents
            
          // rebuild the class path property (i.e. "ScalaSciClassPathProp") in order to account for the new path
          StringBuilder fileStr = new StringBuilder();
      Enumeration enumDirs = GlobalValues.ScalaSciClassPathComponents.elements();
      while (enumDirs.hasMoreElements())  {
         Object ce = enumDirs.nextElement();
         fileStr.append(File.pathSeparator+(String)ce.toString().trim());
    }
      GlobalValues.ScalaSciClassPath = fileStr.toString();   // the ScalaSci classpath as a String
      GlobalValues.settings.setProperty("ScalaSciClassPathProp", GlobalValues.ScalaSciClassPath);    // keep in property in order to be saved
      }   // path not already exist in ScalaSciClassPath
      
           
      // update structures for ScalaSciToolboxes
      if (GlobalValues.ScalaSciToolboxes.contains(forScalaToolboxJarName)==false)        
               {    // path not already exist in ScalaSciUserPaths
          GlobalValues.ScalaSciToolboxes.add(forScalaToolboxJarName);  // append to the ScalaSciUserPaths components for retrieving Scala classes
            
          // rebuild the ScalaSci Toolboxes property (i.e. "ScalaSciToolboxesProp") in order to account for the new path
      StringBuilder fileStr = new StringBuilder();
      Enumeration enumDirs = GlobalValues.ScalaSciToolboxes.elements();
      while (enumDirs.hasMoreElements())  {
         Object ce = enumDirs.nextElement();
         fileStr.append(File.pathSeparator+(String)ce);
    }
      GlobalValues.settings.setProperty("ScalaSciToolboxesProp", fileStr.toString());    // keep in property in order to be saved
      }  // path not already exist in ScalaSciUserPaths
           

               
           }  // for all toolboxes  
     
           // move the .jar toolboxes strings from scalaSciToolboxesAvailableList to scalaSciToolboxesLoadedList
           Enumeration  availableToolboxes =  scalaSciToolboxes.scalaSciToolboxesAvailableListModel.elements();
                 // copy the toolboxes to the loaded list
           while (availableToolboxes.hasMoreElements())   {
               String currentToolbox = (String)availableToolboxes.nextElement();
               scalaSciToolboxes.scalaSciToolboxesLoadedListModel.addElement(currentToolbox);
           }
           scalaSciToolboxes.scalaSciToolboxesAvailableListModel.removeAllElements();   // remove the toolboxes list from the available list


           GlobalValues.scalaJarClassesLoaded = true;    // sets this flag in order to avoid reloading the toolbox list unless there is a change
            

          // display the classes of the available toolboxes using reflection
          for (int k=0; k<scalaSciToolboxes.scalaSciToolboxesLoadedListModel.size(); k++) {
             scalaSciToolbox ssciToolbox = scalaExec.scalaLab.scalaSciToolboxes.ssciToolboxes.elementAt(k);
             String currentToolboxName = ssciToolbox.toolboxName;
             if (scalaSciToolboxes.framesOfToolboxClasses.containsKey(currentToolboxName) == false)  
                { // scan classes if toolbox is not already loaded
            scalaExec.gui.WatchClasses  watchClassesOfToolbox = new scalaExec.gui.WatchClasses();
            
            watchClassesOfToolbox.displayClasses( ssciToolbox.toolboxClasses, ssciToolbox.toolboxName, scalaExec.gui.WatchClasses.watchXLoc+k*50, scalaExec.gui.WatchClasses.watchYLoc+k*50);
             }
             // scalaExec.Interpreter.GlobalValues.autoCompletionScalaSciLoader.loadClasses(ssciToolbox.toolboxName);     // update the autocompletion for the toolbox
       
              }
           
           
         }
             catch (Exception ex)  {
              System.out.println("java.io.IOException in loading Scala jar files");
              ex.printStackTrace();
           }
        }
               
       }

    
    }
       

